<?php
class WebApplication
{
	public $controller;
	public $action;
	public $templates;
	public $interceptors;
	public $request;
	public $response;

	private $router;
	//    public  $default_template = 'smarty'; //smarty or php
	public  $root_dir;
	public  $template_dir;

	public function __construct($root_dir, $template_dir = 'templates') 
	{/*{{{*/
		$this->request = new Request();
		$this->response = new Response();
		$this->templates = array();
		$this->interceptors = array();

		$this->root_dir = $root_dir;
		$this->template_dir = $template_dir;
	}/*}}}*/

	protected function initRouter() { return null; }
	protected function initInterceptors() { return null; }

	public function run() 
	{/*{{{*/
		try {
			$this->dispatch();
			$this->initInterceptors();
			$interceptors = $this->loadInterceptors();
			$this->invokeBefore($interceptors);
			$controller = new $this->controllerClass($this->request, $this->response);
			ob_start();
			$result = null;
			if(false == $this->response->skipAction) {
				$result = $controller->{$this->action}($this->request, $this->response);
			}
			$result = (null==$result)?Controller::SUCCESS:$result;

			if ($this->response->hasRedirect()) {
				ob_end_clean();
				$this->invokeAfter($interceptors);
				$this->response->sendHeaders();
				return;
			}
			if ($result == Controller::DIRECT_OUTPUT) {
				$this->response->sendHeaders();
				ob_end_flush();
				$this->invokeAfter($interceptors);
				return;
			}
			$this->randerView($result);
			$this->invokeAfter($interceptors);
		} catch (WebException $ex) {
			//非法url组合，需要捕获，输出404
			$this->response->ex = $ex;
			$this->randerView(Controller::STATUS_404);
		} catch (Exception $ex) {
			$this->response->ex = $ex;
			$this->randerView();
		}
		ob_end_flush();
	}/*}}}*/

	private function dispatch()
	{/*{{{*/
		$this->router = $this->initRouter();
		$this->router->compile();
		$this->response->setRouter($this->router);
		$script_uri = $this->request->getEnv('SCRIPT_URI');
		//fix script_uri only in apache rewrite model
		if (NULL === $script_uri) {
			$tmpUri = explode('?', getenv('REQUEST_URI'));
			$script_uri = 'http://'.getenv('HTTP_HOST').$tmpUri[0];	//http only
		}
		$this->router->dispatch($this, $script_uri);
		$this->controllerClass = Controller::buildClass($this->controller);
		$this->response->hostname = $this->request->getEnv('HTTP_HOST');
		$this->request->controller = $this->controller;
		$this->request->action = $this->action;
	}/*}}}*/

	private function randerView($result=Controller::SYSTEM_ERROR)
	{/*{{{*/
		$method = 'setFor'.$result;
		if (is_callable(array($this->response, $method))) {
			call_user_func_array(array($this->response, $method), array());
			$this->response->_my_template = '/default/'.$result;
		}
		$this->response->sendHeaders();
		$templateDir = ($this->response->_my_template_dir) ? $this->response->_my_template_dir : $this->template_dir;
		$template = ($this->response->_my_template) ? $this->response->_my_template : $this->templates[$result];
		$view = View::getInstance("{$this->root_dir}/".$templateDir, $template);
		$view->rander($this->response->getModel());
	}/*}}}*/
    
	private function loadInterceptors() 
	{/*{{{*/
		$interceptors = array();
        
		if (isset($this->interceptors[$this->controller][$this->action])) {
			$interceptorNames = $this->interceptors[$this->controller][$this->action];
		} else if (isset($this->interceptors[$this->controller]['*'])) {
			$interceptorNames = $this->interceptors[$this->controller]['*'];
		} else {
			$interceptorNames = $this->interceptors['default'];
		}
		if (empty($interceptorNames))
			$interceptorNames = array();
		foreach ($interceptorNames as $interceptorName) {
			$interceptors[] = new $interceptorName();
		}
		return $interceptors;
	}/*}}}*/

	private function invokeBefore($interceptors) 
	{/*{{{*/
		foreach ($interceptors as $interceptor) {
			$interceptor->before($this);
		}
	}/*}}}*/

	private function invokeAfter($interceptors) 
	{/*{{{*/
		while (($interceptor = array_pop($interceptors)) == true) {
			$interceptor->after($this);
		}
	}/*}}}*/
}
?>
